/*******************************************************************************
 * This file is part of Crunch Graphics.
 *
 * Crunch Graphics is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Lesser General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later version.
 *
 * Crunch Graphics is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
 * PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with
 * Crunch Graphics.  If not, see <http://www.gnu.org/licenses/>.
 ******************************************************************************/

package com.crunch.graphics.gpu;

import javax.media.opengl.GL;
import javax.media.opengl.GL3;
import javax.media.opengl.GL4;

/** Encapsulates a texture image unit on the GPU.
 */
public class GpuTextureImageUnit extends ReferenceCountedGpuObject {
	/** Sets this to be the active texture image unit.
	 */
	public void setActive() {
		getGpuState().setActiveTextureImageUnitIndex(index);
	}

	/** Returns whether this is the active texture image unit.
	 */
	public boolean isActive() {
		return index == getGpuState().getActiveTextureImageUnitIndex();
	}

	/** Binds the given texture to this texture image unit.
	 */
	public void bindTexture(GpuTexture texture) {
		int loc = getTextureBindingLocation(texture.getType()).ordinal();
		if (textureBindingLocations[loc] == texture) {
			// already bound
			return;
		}

		setActive();
		getGpuState().getGl().glBindTexture(texture.getType(), texture.getId());
		texture.acquire();
		if (textureBindingLocations[loc] != null) {
			textureBindingLocations[loc].release();
		}
		textureBindingLocations[loc] = texture;
	}

	/** Unbinds the currently bound texture from this texture image unit.
	 */
	public void unbindTexture(int location) {
		int loc = getTextureBindingLocation(location).ordinal();
		if (textureBindingLocations[loc] == null) {
			// already unbound
			return;
		}

		setActive();
		getGpuState().getGl().glBindTexture(location, 0);
		textureBindingLocations[loc].release();
		textureBindingLocations[loc] = null;
	}

	/** Returns the texture currently bound to the given location.
	 */
	public GpuTexture getBoundTexture(int location) {
		return textureBindingLocations[getTextureBindingLocation(location).ordinal()];
	}

	GpuTextureImageUnit(GpuState state, int i) {
		super(state);
		index = i;
	}

	protected void onRelease() {
	}

	private enum TextureBindingLocation {
		TEXTURE_1D,
		TEXTURE_1D_ARRAY,
		TEXTURE_2D,
		TEXTURE_2D_ARRAY,
		TEXTURE_2D_MULTISAMPLE,
		TEXTURE_2D_MULTISAMPLE_ARRAY,
		TEXTURE_3D,
		TEXTURE_BUFFER,
		TEXTURE_CUBE_MAP,
		TEXTURE_CUBE_MAP_ARRAY,
		TEXTURE_RECTANGLE,
	}
	private static TextureBindingLocation getTextureBindingLocation(int loc) {
		switch (loc) {
			case GL3.GL_TEXTURE_1D:
				return TextureBindingLocation.TEXTURE_1D;
			case GL3.GL_TEXTURE_1D_ARRAY:
				return TextureBindingLocation.TEXTURE_1D_ARRAY;
			case GL.GL_TEXTURE_2D:
				return TextureBindingLocation.TEXTURE_2D;
			case GL.GL_TEXTURE_2D_ARRAY:
				return TextureBindingLocation.TEXTURE_2D_ARRAY;
			case GL3.GL_TEXTURE_2D_MULTISAMPLE:
				return TextureBindingLocation.TEXTURE_2D_MULTISAMPLE;
			case GL3.GL_TEXTURE_2D_MULTISAMPLE_ARRAY:
				return TextureBindingLocation.TEXTURE_2D_MULTISAMPLE_ARRAY;
			case GL3.GL_TEXTURE_3D:
				return TextureBindingLocation.TEXTURE_3D;
			case GL3.GL_TEXTURE_BUFFER:
				return TextureBindingLocation.TEXTURE_BUFFER;
			case GL.GL_TEXTURE_CUBE_MAP:
				return TextureBindingLocation.TEXTURE_CUBE_MAP;
			case GL4.GL_TEXTURE_CUBE_MAP_ARRAY:
				return TextureBindingLocation.TEXTURE_CUBE_MAP_ARRAY;
			case GL3.GL_TEXTURE_RECTANGLE:
				return TextureBindingLocation.TEXTURE_RECTANGLE;
			default:
				throw new IllegalArgumentException("Invalid texture binding location" );
		}
	}

	private int index;
	GpuTexture[] textureBindingLocations = new GpuTexture[TextureBindingLocation.values().length];
}